#pragma once

#include "Rpg/Core.h"


namespace Rpg {
	enum class EventType {
		None = 0,
		WindowClose,
		WindowResize,
		WindowFocus,
		WindowLostFocus,
		WindowMove,
		AppTick,
		AppUpdate,
		AppRender,
		KeyPressed,
		KeyReleased,
		KeyTyped,
		MouseButtonPressed,
		MouseButtonReleased,
		MouseMoved,
		MouseScrolled
	};

	enum EventCategory {
		None = 0,
		EventCategoryApplication	= BIT(0), // 1
		EventCategoryInput			= BIT(1), // 4
		EventCategoryKeyboard		= BIT(2), // 8
		EventCategoryMouse			= BIT(3), // 16
		EventCategoryMouseButton    = BIT(4)  // 32
	};

#define EVENT_CLASS_TYPE(type) static EventType GetStaticType(){return EventType::##type;} \
									virtual EventType GetEventType() const override {return GetStaticType();}\
									virtual const char* GetName() const override { return #type; }

#define EVENT_CLASS_CATEGORY(category) virtual int GetCategoryFlags() const override { return category; }

	class RPG_API Event {
	public:
		bool m_Handle = false;
		virtual EventType GetEventType() const = 0;
		virtual const char* GetName() const = 0;
		virtual int GetCategoryFlags() const = 0;
		virtual std::string ToString() const { return GetName(); };
		
		inline bool IsInCategory(EventCategory category) {
			return GetCategoryFlags() & category;
		}
	};

	class EventDispatcher {
		template<typename T>
		using EventFn = std::function<bool(T&)>;
	public:
		EventDispatcher(Event& event) :m_Event(event) {

		}

		template<typename T>
		bool Dispatch(EventFn<T> fn) {
			if (m_Event.GetEventType() == T::GetStaticType()) {
				m_Event.m_Handle = fn(*(T*)&m_Event);
				return true;
			}
			return false;
		}
	private:
		Event& m_Event;
	};

	inline std::ostream& operator<<(std::ostream& stream, const Event& e) {
		 
		 return stream << e.ToString();
	}
}